/*
 *  Copyright (c) 2022 ZhuHai Jieli Technology Co.,Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "csfr.h"
#include "typedef.h"
#include "spinlock.h"
#include "stdio.h"
#include "stdarg.h"

#define LOG_TAG_CONST       DEBUG
#define LOG_TAG             "[DEBUG]"
#define LOG_ERROR_ENABLE
#define LOG_INFO_ENABLE
#include "debug.h"

#define log_d printf

#define UART_BUF        JL_UART0->BUF
#define UART_CON        JL_UART0->CON0
#define UART_BAUD       JL_UART0->BAUD

#define AT_RAM      // AT(.volatile_ram_code)

AT_RAM
void sputchar(char c)
{
    UART_BUF = c;
    __asm__ volatile("csync");
    while ((UART_CON & BIT(15)) == 0);
    UART_CON |= BIT(13);
}

AT_RAM
static void sput_hex(u32 dat, u8 len)
{
    for (s32 i = len - 1; i >= 0; i--) {
        u8 val = 0xf & (dat >> (i * 4));
        if (val > 9) {
            sputchar(val - 10 + 'A');
        } else {
            sputchar(val + '0');
        }
    }
}

AT_RAM
_INLINE_
static int sputs(const char *s)
{
    char c;
    const char *ser = s;
    while (1) {
        c = *s;
        if (c) {
            sputchar(c);
            s++;
        } else {
            break;
        }
    }
    return (int)s - (int)ser;
}

AT_RAM
static void sput_u8hex(u8 dat)
{
    sputchar('0');
    sputchar('x');
    sput_hex(dat, 2);
    sputchar(' ');
}

AT_RAM
static void sput_u16hex(u16 dat)
{
    sputchar('0');
    sputchar('x');
    sput_hex(dat, 4);
    sputchar(' ');
}

AT_RAM
void sput_u32hex(u32 dat)
{
    sputchar('0');
    sputchar('x');
    sput_hex(dat, 8);
    sputchar(' ');
}

AT_RAM
void sput_u64hex(u64 dat)
{
    sputchar('0');
    sputchar('x');
    sput_hex(dat >> 32, 8);
    sput_hex(dat, 8);
    sputchar(' ');
}

AT_RAM
void sput_buf(const u8 *buf, int len)
{
    for (int i = 0; i < len; i++) {
        if ((i % 16) == 0) {
            sputchar('\n') ;
        }
        sput_hex(buf[i], 2);
        sputchar(' ');
    }
    sputchar('\n') ;
}
/* ----------------------------------------------------------- */

#define DEBUG_WR_SFR_EN     do {JL_DEBUG->WR_EN = 0xE7;} while (0)

#define _DSP_BF_CON                JL_DEBUG->DSP_BF_CON
#define _WR_EN                    JL_DEBUG->WR_EN
#define _DEBUG_MSG                JL_DEBUG->MSG
#define _DEBUG_MSG_CLR            JL_DEBUG->MSG_CLR
#define _DSP_PC_LIML0            JL_DEBUG->DSP_PC_LIML0
#define _DSP_PC_LIMH0            JL_DEBUG->DSP_PC_LIMH0
#define _DSP_PC_LIML1            JL_DEBUG->DSP_PC_LIML1
#define _DSP_PC_LIMH1            JL_DEBUG->DSP_PC_LIMH1
#define _DSP_EX_LIML            JL_DEBUG->DSP_EX_LIML
#define _DSP_EX_LIMH            JL_DEBUG->DSP_EX_LIMH
#define _PRP_EX_LIML            JL_DEBUG->PRP_EX_LIML
#define _PRP_EX_LIMH            JL_DEBUG->PRP_EX_LIMH
#define _PRP_MMU_MSG            JL_DEBUG->PRP_MMU_MSG
#define _LSB_MMU_MSG_CH            JL_DEBUG->LSB_MMU_MSG_CH
#define _PRP_WR_LIMIT_MSG        JL_DEBUG->PRP_WR_LIMIT_MSG
#define _LSB_WR_LIMIT_CH        JL_DEBUG->LSB_WR_LIMIT_CH
#define _PRP_SRM_INV_MSG        JL_DEBUG->PRP_SRM_INV_MSG
#define _LSB_SRM_INV_CH        JL_DEBUG->LSB_SRM_INV_CH
#define _DSPCON                    JL_DSP->CON
#define _EMU_CON                q32DSP(0)->EMU_CON
#define _EMU_MSG                q32DSP(0)->EMU_MSG
#define _EMU_SSP_H              q32DSP(0)->EMU_SSP_H
#define _EMU_SSP_L              q32DSP(0)->EMU_SSP_L
#define _EMU_USP_H              q32DSP(0)->EMU_USP_H
#define _EMU_USP_L              q32DSP(0)->EMU_USP_L
#define _ETM_CON                q32DSP(0)->ETM_CON

#define debug_log     log_d

static char *const  debug_msg[32] = {
    "reserved",            // 31
    "access_IcacheRam_or_DcacheRam",    // 30
    "reserved",            // 29
    "access_sbcRam_is_not_sbc",            // 28

    "reserved",            // 27
    "cpu_write_reserved_address",        // 26
    "cpu_read_reserved_address",        // 25
    "cpu_instruction_fetch_from_reserved_address",            // 24

    "reserved",         // 23
    "reserved",         // 22
    "reserved",         // 21
    "peripheral_access_reserved_address",                    // 20

    "peripheral_write_external_memory_when_cache_disable",    // 19
    "peripheral_read_external_memory_when_cache_disable",    // 18
    "cpu_write_data_over_limit",        // 17
    "cpu_execute_over_limit",            // 16

    "reserved",         // 15
    "reserved",         // 14
    "reserved",         // 13
    "reserved",         // 12

    "reserved",         // 11
    "reserved",         // 10
    "peripheral_access_mmu_error",        // 9
    "cpu_access_mmu_error",                // 8

    "reserved",         // 7
    "reserved",         // 6
    "reserved",         // 5
    "reserved",         // 4

    "reserved",         // 3
    "peripheral_write_data_over_limit",    // 2
    "reserved",            // 1
    "watchdog",            // 0
};

static char *const prp_mmu_err_msg[32] = {
    "reserved",        // 31
    "reserved",        // 30
    "reserved",        // 29
    "reserved",        // 28

    "reserved",        // 27
    "reserved",        // 26
    "reserved",        // 25
    "reserved",        // 24

    "reserved",        // 23
    "reserved",        // 22
    "reserved",        // 21
    "reserved",        // 20

    "reserved",        // 19
    "reserved",        // 18
    "reserved",        // 17
    "reserved",        // 16

    "reserved",        // 15
    "reserved",        // 14
    "CPU_IF",        // 13
    "CPU_RD",        // 12

    "CPU_WR",        // 11
    "reserved",        // 10
    "FIR",            // 9
    "EQ",            // 8

    "FFT",          // 7
    "BT",            // 6
    "FM",              // 5
    "LSB_LG1",        // 4

    "LSB_LG0",        // 3
    "DCP_RD",        // 2
    "DCP_WR",        // 1
    "USB",            // 0
};

static char *const prp_err_msg[32] = {
    "reserved",        // 31
    "reserved",        // 30
    "reserved",        // 29
    "reserved",        // 28

    "reserved",        // 27
    "reserved",        // 26
    "reserved",        // 25
    "reserved",        // 24

    "reserved",        // 23
    "reserved",        // 22
    "reserved",        // 21
    "reserved",        // 20

    "reserved",        // 19
    "reserved",        // 18
    "reserved",        // 17
    "DCP_CACHE",    // 16

    "DCP_SRM",        // 15
    "reserved",        // 14
    "reserved",        // 13
    "reserved",        // 12

    "reserved",        // 11
    "reserved",        // 10
    "reserved",        // 9
    "FM",            // 8

    "BT",              // 7
    "reserved",        // 6
    "FIR",          // 5
    "FFT",            // 4

    "EQ",            // 3
    "USB",            // 2
    "LSB_LG1",        // 1
    "LSB_LG0",        // 0
};

static char *const lsb_err_msg[32] = {
    "reserved",        // 31
    "reserved",        // 30
    "reserved",        // 29
    "reserved",        // 28

    "reserved",        // 27
    "reserved",        // 26
    "reserved",        // 25
    "reserved",        // 24

    "CTMU",         // 23
    "UART2_RD",        // 22
    "UART2_WR",        // 21
    "UART1_RD",        // 20

    "UART1_WR",        // 19
    "UART0_RD",        // 18
    "UART0_WR",        // 17
    "SPI2",            // 16

    "SPI1",         // 15
    "SPI0",            // 14
    "SD1_D",        // 13
    "SD1_C",        // 12

    "SD0_D",        // 11
    "SD0_C",        // 10
    "SBC",          // 9
    "PLNK",            // 8

    "PAP",            // 7
    "ISP",          // 6
    "SPDIF_I",        // 5
    "SPDIF_D",        // 4

    "AUDIO",        // 3
    "ALNK1",        // 2
    "ALNK0",        // 1
    "reserved",        // 0
};

static char *const emu_msg[32] = {
    "reserved",        // 31
    "reserved",        // 30
    "reserved",        // 29
    "reserved",        // 28

    "reserved",        // 27
    "reserved",        // 26
    "reserved",        // 25
    "reserved",        // 24

    "reserved",        // 23
    "reserved",        // 22
    "reserved",        // 21
    "fpu_ine_err",    // 20

    "fpu_huge_err",    // 19
    "fpu_tiny",        // 18
    "fpu_inf_err",    // 17
    "fpu_inv_err",    // 16

    "reserved",        // 15
    "reserved",        // 14
    "reserved",        // 13
    "reserved",        // 12

    "reserved",        // 11
    "reserved",        // 10
    "reserved",        // 9
    "reserved",        // 8

    "reserved",        // 7
    "reserved",        // 6
    "reserved",        // 5
    "reserved",        // 4

    "reserved",        // 3
    "div0_err",        // 2
    "illeg_err",    // 1
    "misalign_err",    // 0
};

/********************************* DEBUG PART **********************************/
static void debug_enter_critical(void)
{
    while (!(JL_DEBUG->WR_EN & BIT(0))) {
        DEBUG_WR_SFR_EN;
    }
}

static void debug_exit_critical()
{
    while (JL_DEBUG->WR_EN & BIT(0)) {
        DEBUG_WR_SFR_EN;
    }
}
__attribute__((noinline)) void sp_ovf_unen(void)
{
    q32DSP(current_cpu_id())->EMU_CON &= ~ BIT(3);
}

__attribute__((noinline)) void sp_ovf_en(void)
{
#ifndef SDTAP_DEBUG
    q32DSP(current_cpu_id())->EMU_CON |= BIT(3); // 如果用户使用setjmp longjmp, 或者使用sdtap gdb调试 务必要删掉这句话
#endif
}
static void pc_rang_limit0(void *low_addr, void *high_addr)
{
    debug_enter_critical();
    _DSP_PC_LIML0 = (u32)low_addr;
    _DSP_PC_LIMH0 = (u32)high_addr;
    debug_exit_critical();
}

static void pc_rang_limit1(void *low_addr, void *high_addr)
{
    debug_enter_critical();
    _DSP_PC_LIML1 = (u32)low_addr;
    _DSP_PC_LIMH1 = (u32)high_addr;
    debug_exit_critical();
}

static u32 flag1;
static u32 suspend_cnt1;

void prp_store_rang_limit_set(void *low_addr, void *high_addr, u8 mode)
{
    debug_enter_critical();

    _DSP_BF_CON &= ~BIT(8);

    _PRP_EX_LIML = (u32)low_addr;
    _PRP_EX_LIMH = (u32)high_addr;

    if (mode) {
        _DSP_BF_CON |= BIT(9);  // 写框内触发
    } else {
        _DSP_BF_CON &= ~BIT(9); // 写框外触发
    }

    _DSP_BF_CON |= BIT(8);

    debug_exit_critical();
}

void prp_protect_ram_suspend(void)
{
    debug_enter_critical();
    // restore sfr setting
    flag1 = _DSP_BF_CON;
    suspend_cnt1++;
    // disable if enable
    if (flag1 & BIT(8)) {
        _DSP_BF_CON &= ~BIT(8);
    }

    debug_exit_critical();
}

void prp_protect_ram_resume(void)
{
    debug_enter_critical();
    ASSERT(suspend_cnt1, "\r\n----use when no suspend %s %x %x\r\n", __func__, suspend_cnt1, _DSP_BF_CON);

    if (flag1 & BIT(8)) {
        _DSP_BF_CON |= BIT(8);
    }

    suspend_cnt1--;
    debug_exit_critical();
}

static u32 flag;
static u32 suspend_cnt;

void cpu_store_rang_limit_set(void *low_addr, void *high_addr, u8 inside)
{
    debug_enter_critical();

    _DSP_BF_CON &= ~BIT(10);

    _DSP_EX_LIML = (u32)low_addr;
    _DSP_EX_LIMH = (u32)high_addr;

    if (inside) {
        _DSP_BF_CON |= BIT(11);
    } else {
        _DSP_BF_CON &= ~BIT(11);
    }

    _DSP_BF_CON |= BIT(10);

    debug_exit_critical();
}

void cpu_protect_ram_suspend(void)
{
    debug_enter_critical();
    // restore sfr setting
    flag = _DSP_BF_CON;
    suspend_cnt++;
    // disable if enable
    if (flag & BIT(10)) {
        _DSP_BF_CON &= ~BIT(10);
    }

    debug_exit_critical();
}

void cpu_protect_ram_resume(void)
{
    debug_enter_critical();
    ASSERT(suspend_cnt, "\r\n----use when no suspend %s %x %x\r\n", __func__, suspend_cnt, _DSP_BF_CON);

    if (flag & BIT(10)) {
        _DSP_BF_CON |= BIT(10);
    }

    suspend_cnt--;
    debug_exit_critical();
}

void icache_exception_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _DSPCON |= BIT(30);
    } else {
        _DSPCON &= ~BIT(30);
    }
    debug_exit_critical();
}
void peripheral_access_MMU_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _DSPCON |= BIT(29);
    } else {
        _DSPCON &= ~BIT(29);
    }
    debug_exit_critical();
}

void perpheral_access_invalid_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _DSPCON |= BIT(28);
    } else {
        _DSPCON &= ~BIT(28);
    }
    debug_exit_critical();
}

void cpu_load_ins_invalid_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _DSPCON |= BIT(27);
    } else {
        _DSPCON &= ~BIT(27);
    }
    debug_exit_critical();
}

void cpu_load_data_invalid_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _DSPCON |= BIT(26);
    } else {
        _DSPCON &= ~BIT(26);
    }
    debug_exit_critical();
}

void cpu_store_invalid_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _DSPCON |= BIT(25);
    } else {
        _DSPCON &= ~BIT(25);
    }
    debug_exit_critical();
}

void peripheral_cpu_access_invalid_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _DSPCON |= BIT(24);
    } else {
        _DSPCON &= ~BIT(24);
    }
    debug_exit_critical();
}

/******************************** EMU PART  *****************************/
void emu_misalign_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _EMU_CON |= BIT(0);
    } else {
        _EMU_CON &= ~BIT(0);
    }
    debug_exit_critical();
}

void emu_illeg_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _EMU_CON |= BIT(1);
    } else {
        _EMU_CON &= ~BIT(1);
    }
    debug_exit_critical();
}

void emu_div0_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _EMU_CON |= BIT(2);
    } else {
        _EMU_CON &= ~BIT(2);
    }
    debug_exit_critical();
}

void emu_fpu_inv_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _EMU_CON |= BIT(16);
    } else {
        _EMU_CON &= ~BIT(16);
    }
    debug_exit_critical();
}

void emu_fpu_inf_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _EMU_CON |= BIT(17);
    } else {
        _EMU_CON &= ~BIT(17);
    }
    debug_exit_critical();
}

void emu_fpu_tiny_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _EMU_CON |= BIT(15);
    } else {
        _EMU_CON &= ~BIT(15);
    }
    debug_exit_critical();
}

void emu_fpu_huge_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _EMU_CON |= BIT(19);
    } else {
        _EMU_CON &= ~BIT(19);
    }
    debug_exit_critical();
}

void emu_fpu_ine_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _EMU_CON |= BIT(20);
    } else {
        _EMU_CON &= ~BIT(20);
    }
    debug_exit_critical();
}

void emu_stack_limit_set(u8 mode, u32 limit_l, u32 limit_h)
{
    debug_enter_critical();
    if (mode) {
        _EMU_SSP_H = limit_h;
        _EMU_SSP_L = limit_l;
    } else {
        _EMU_USP_H = limit_h;
        _EMU_USP_L = limit_l;
    }
    debug_exit_critical();
}

void etm_pc_trace_enable(u8 enable)
{
    debug_enter_critical();
    if (enable) {
        _ETM_CON |= BIT(0);
    } else {
        _ETM_CON &= ~BIT(0);
    }
    debug_exit_critical();
}

/**************************************************/
#define STACK_MAGIC     0x5a5a5a5a
extern u32 stack_magic[4];
extern u32 stack_magic0[4] ;
#define TRIGGER         __asm__ volatile ("trigger")

u32 data_magic[16] sec(.data_magic) ;
AT_RAM
void trace_call_stack(void)
{
    sputchar('\n');
    for (int c = 0; c < CPU_CORE_NUM; c++) {
        sput_u32hex(q32DSP(c)->ETM_PC3);
        sputchar('-');
        sputchar('>');
        sput_u32hex(q32DSP(c)->ETM_PC2);
        sputchar('-');
        sputchar('>');
        sput_u32hex(q32DSP(c)->ETM_PC1);
        sputchar('-');
        sputchar('>');
        sput_u32hex(q32DSP(c)->ETM_PC0);
    }
    sputchar('\n');
}

AT_RAM
void __attribute__((weak)) exception_analyze_user(void)
{
}

AT_RAM
void __attribute__((weak)) exception_analyze_release(void)
{
    cpu_reset();
}

extern const int config_asser;
AT_RAM
void exception_analyze(unsigned int *sp)
{
    if (config_asser) {
        unsigned int reti = sp[16];
        unsigned int rete = sp[17];
        unsigned int retx = sp[18];
        unsigned int rets = sp[19];
        unsigned int psr  = sp[20];
        unsigned int icfg = sp[21];
        unsigned int usp  = sp[22];
        unsigned int ssp  = sp[23];

        int i;

        sputs("\r\nXX\r\n");
        sput_u32hex(stack_magic[0]);
        if (stack_magic[0] != STACK_MAGIC) {
            sputs("\r\nStack Magic destory!");
        }

        sput_u32hex(stack_magic0[0]);
        if (stack_magic0[0] != STACK_MAGIC) {
            sputs("\r\nStack Magic0 destory!");
        }

        sputs("JL_LRCT->CON:");
        sput_u32hex(JL_LRCT->CON);

        sput_u32hex(data_magic[0]);
        sputs("\r\nusp : ");
        sput_u32hex(usp);
        sputs("\r\nssp : ");
        sput_u32hex(ssp);
        sputs("\r\nsp : ");
        sput_u32hex((u32)sp);
        sputs("\r\nStack : ");
        extern int _stack_end;
        sput_u32hex((u32)&_stack_end);
        sputs("\r\nrets : ");
        sput_u32hex(rets);
        sputs("\r\nreti : ");
        sput_u32hex(reti);
        sputs("\r\nretx : ");
        sput_u32hex(retx);
        sputs("\r\nrete : ");
        sput_u32hex(rete);
        sputs("\r\npsr : ");
        sput_u32hex(psr);
        sputs("\r\nicfg : ");
        sput_u32hex(icfg);
        trace_call_stack();

        sputs("\n--i--- \n ");
        sput_buf((u8 *)reti - 0x20, 0x40);

        sputs("\n--r--- \n ");
        sput_buf((u8 *)rets - 0x20, 0x40);

        sputs("\r\nDEBUG_MSG ");
        sput_u32hex(_DEBUG_MSG);
        sputs("\r\nDSPCON ");
        sput_u32hex(_DSPCON);
        sputs("\r\nEMU_CON ");
        sput_u32hex(_EMU_CON);
        sputs("\r\nEMU_MSG ");
        sput_u32hex(_EMU_MSG);
        sputs("\r\nPRP_MMU_MSG ");
        sput_u32hex(_PRP_MMU_MSG);
        sputs("\r\nLSB_MMU_MSG_CH ");
        sput_u32hex(_LSB_MMU_MSG_CH);
        sputs("\r\nPRP_WR_LIMIT_MSG ");
        sput_u32hex(_PRP_WR_LIMIT_MSG);
        sputs("\r\nLSB_WR_LIMIT_CH ");
        sput_u32hex(_LSB_WR_LIMIT_CH);
        sputs("\r\nPRP_SRM_INV_MSG ");
        sput_u32hex(_PRP_SRM_INV_MSG);
        sputs("\r\nLSB_SRM_INV_CH ");
        sput_u32hex(_LSB_SRM_INV_CH);

        sputs("\r\nZZ");

        for (i = 0; i < 32; i++) {
            if (_DEBUG_MSG & BIT(i)) {
                sputs("\r\ndebug err msg : ");
                sputs(debug_msg[31 - i]);
            }
        }

        for (i = 0; i < 32; i++) {
            if (_EMU_MSG & BIT(i)) {
                sputs("\r\nemu err msg : ");
                sputs(emu_msg[31 - i]);
            }
        }

        if (_DEBUG_MSG & BIT(9)) {
            for (i = 0; i < 32; i++) {
                if (_PRP_MMU_MSG & BIT(i)) {
                    sputs("\r\nprp wr mmu err msg : ");
                    sputs(prp_mmu_err_msg[31 - i]);
                }
            }
            // lsb group 1&0
            if ((_PRP_MMU_MSG & BIT(3)) || (_PRP_MMU_MSG & BIT(4))) {
                sputs("\r\nlsb wr mmu err msg : ");
                sputs(lsb_err_msg[31 - (_LSB_MMU_MSG_CH & 0xff)]);
            }
        }

        if (_DEBUG_MSG & BIT(2)) {
            for (i = 0; i < 32; i++) {
                if (_PRP_WR_LIMIT_MSG & BIT(i)) {
                    sputs("\r\nprp wr limit err msg : ");
                    sputs(prp_err_msg[31 - i]);
                }
            }
            // lsb group 1&0
            if ((_PRP_WR_LIMIT_MSG & BIT(1)) || (_PRP_WR_LIMIT_MSG & BIT(0))) {
                sputs("\r\nlsb wr limit err msg : ");
                sputs(lsb_err_msg[31 - (_LSB_WR_LIMIT_CH & 0xff)]);
            }
        }

        for (i = 0; i < 16; ++i) {
            sputs("\r\n");
            sput_hex(i, 1);
            sputchar('=');
            sput_u32hex(sp[i]);
        }

        for (i = 0; i < ARRAY_SIZE(stack_magic); i++) {
            if (stack_magic[i] != 0x5a5a5a5a) {
                sputs("\r\nstack_magic");
                sput_u8hex(i);
                sput_u32hex(stack_magic[i]);
            }
        }

        for (i = 0; i < ARRAY_SIZE(stack_magic0); i++) {
            if (stack_magic0[i] != 0x5a5a5a5a) {
                sputs("\r\nstack_magic0");
                sput_u8hex(i);
                sput_u32hex(stack_magic0[i]);
            }
        }

        for (i = 0; i < ARRAY_SIZE(data_magic); i++) {
            if (data_magic[i] != 0xffffffff) {
                sputs("\r\ndata_magic");
                sput_u8hex(i);
                sput_u32hex(data_magic[i]);
            }
        }

        extern int _data_code_begin, _data_code_mid, _data_code_end;
        int data_code_len = (void *)&_data_code_end - (void *)&_data_code_mid;
        u8 *code_ptr = (void *)&_data_code_mid;
        u8 *data_ptr = (u8 *)sizeof(data_magic);

        sput_u32hex(*((u32 *)0x17ce0));

        // 把ssp, usp 的一部分内容也打印出来
        sputs("\r\nusp = ");
        sputs("\r\nssp = ");
        exception_analyze_user();

        while (1) {
        }
    } else {
        exception_analyze_release();
    }
}

static void exception_cpu_ex_over_limit_error(void)
{
    // range outside
    TRIGGER;
    TRIGGER;
    void (*foo)(void);
    foo = (void (*)(void))0x18001;
    foo();
}

static void exception_cpu_store_error(void)
{
    log_d(">>>>exception_cpu_store_error");
    cpu_store_rang_limit_set((void *)0x100, (void *)0x104 - 1, 1);

    log_d(">>>>ready");
    TRIGGER;
    TRIGGER;
    *((u32 *)0x108) = 1;
    log_d(">>>>pass");
}

__attribute__((noinline)) static int div0_test(int c)
{
    ___trig;
    int a;
    int b = 3;
    a = c;
    b = b - c;
    log_d(" %d / %d = %d", a, b, a / b);
    ___trig;
    return a;
}

volatile u8  etm_point_w_8  ; // = (volatile u8 *)0x4100001;
volatile u16 etm_point_w_16 ; // = (volatile u16 *)0x4100002;
volatile int etm_point_w_32 ; // = (volatile int *)0x4100004;
volatile u8  etm_point_r_8  ; // = (volatile u8 *)0x4100011;
volatile u16 etm_point_r_16 ; // = (volatile u16 *)0x4100012;
volatile int etm_point_r_32 ; // = (volatile int *)0x4100014;
__attribute__((noinline)) void watch_point_test(void)
{
    int i = 0;
    while (1) {
        log_d("cpu id %d\n", current_cpu_id());
        etm_point_w_32 = i;
        etm_point_w_16 = i;
        etm_point_w_8 = i;
        __asm__ volatile("idle");
        log_d("i= %d\n", i);
        i++;

        log_d("cpu id %d\n", current_cpu_id());
        log_d("%s() %d\n\r", __func__, etm_point_r_8);
        log_d("%s() %d\n\r", __func__, etm_point_r_16);
        log_d("%s() %d\n\r", __func__, etm_point_r_32);
    }
}

void debug_sfr_test(void)
{
    log_d("debug sfr test");

    // 非对齐访问
#if 1
    // miss aline
    volatile u32 *p = (volatile u32 *)0x1;
    // 编译器会编译为对齐
    *p = 0x12345678;
    int v = 0x12345678;
#endif
}

extern int text_begin;
extern int text_end;
extern int _data_code_begin;
extern int _data_code_end;
extern int _cpu_store_begin, _cpu_store_end;
extern int _prp_store_begin, _prp_store_end;
extern int _nv_pre_begin, _nv_pre_end;
extern u32 _cpu0_sstack_begin;
extern u32 _cpu0_sstack_end;

void debug_dump(void)
{
    log_d("DSP_CON : 0x%x", _DSPCON);
    log_d("EMU_CON : 0x%x", _EMU_CON);
    log_d("ETM_CON : 0x%x", _ETM_CON);
    log_d("EMU_MSG : 0x%x", _EMU_MSG);
    log_d("DEBUG_MSG : 0x%x", _DEBUG_MSG);
    log_d("PRP_MMU_MSG : 0x%x", _PRP_MMU_MSG);
    log_d("LSB_MMU_MSG_CH : 0x%x", _LSB_MMU_MSG_CH);
    log_d("PRP_WR_LIMIT_MSG : 0x%x", _PRP_WR_LIMIT_MSG);
}
void ram_protect_close(void)
{
    debug_enter_critical();
    _DSPCON &= ~(0b1111111 << 24);
    _DSP_BF_CON = 0;
    _EMU_CON = 3;
    pc_rang_limit0((void *)0, (void *)0x1ffffff);
    pc_rang_limit1((void *)0, (void *)0x1ffffff);
    emu_stack_limit_set(0, 0x0, 0x1ffffff);
    emu_stack_limit_set(1, 0x0, 0x1ffffff);
    debug_exit_critical();
}

void debug_init(void)
{
    log_d(">>> debug init ");

    // DEBUG
    peripheral_access_MMU_enable(1);
    perpheral_access_invalid_enable(1);
    cpu_load_ins_invalid_enable(1);
    cpu_load_data_invalid_enable(1);
    cpu_store_invalid_enable(1);
    peripheral_cpu_access_invalid_enable(1);

    // EMU
    emu_misalign_enable(1);
    emu_illeg_enable(1);
    emu_div0_enable(1);

    // !!! br23/br25不能打开浮点异常使能 !!!
    // ETM
    etm_pc_trace_enable(1);

#ifndef CONFIG_BP_DEBUG_ENABLE
    // CPU execute protect
    pc_rang_limit0(&text_begin, &text_end);
#else
    // 使用uboot的断点功能因为不知道使用者代码的安放位置，暂时关闭地址限制
    // CPU execute protect
#endif
    log_d("pc_limit0: 0x%x ~ 0x%x",  _DSP_PC_LIML0 & 0x1ffffff, _DSP_PC_LIMH0 & 0x1ffffff);
    log_d("pc_limit1: 0x%x ~ 0x%x",  _DSP_PC_LIML1 & 0x1ffffff, _DSP_PC_LIMH1 & 0x1ffffff);

    memset_s(data_magic, sizeof(data_magic), 0xff, sizeof(data_magic));

    // cpu store protect
    cpu_store_rang_limit_set((void *)&_data_code_begin, (void *)&_data_code_end - 1, 1);

    // prp store protect
    prp_store_rang_limit_set((void *)&_data_code_begin, (void *)&_data_code_end - 1, 1);

    emu_stack_limit_set(1, (u32)&_cpu0_sstack_begin, (u32)&_cpu0_sstack_end);
}
